/**
 *    Copyright 2009-2020 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.aapl.script;

import org.apache.ibatis.aapl.util.ReflectUtil;
import org.apache.ibatis.aapl.util.StormAnnotationUtil;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.*;

/**
 * 根据分析，在将方法上的SQL提取出来的时候。
 * knownMapper中已经包含了该Mapper。
 * 但是无法直接确定knownMapper中究竟哪一个是我们需要的Mapper。
 * <p>
 * 因此在每次执行到createSqlSource的时候我们都需要判断。
 * <p>
 * 首先：
 * 第一次执行到createSqlSource的时候，先检查是否包含了有效的Mapper，没有包含有效的Mapper
 * 则将Mapper都放入到MapperEvaluator中去，由MapperEvaluator筛选出有效的Mapper，
 * 并将该Mapper设置为currentMapper。
 */
public class MapperEvaluator {
  private Set<Class<?>> parsed = new HashSet<>();

  public Class<?> evaluate(Collection<Class<?>> classes) {
    List<Class<?>> lists = new LinkedList<>();
    for (Class<?> clazz : classes) {
      if (!parsed.contains(clazz)) {
        lists.add(clazz);
        parsed.add(clazz);
      }
    }
    return validClass(lists);
  }

  public Map<String, List<Method>> scriptMethod(Class<?> type) {
    Map<String, List<Method>> map = new HashMap<>();
    Method[] methods = type.getMethods();
    for (Method method : methods) {
      String script = getScript(method);
      if (script != null) {
        List<Method> ms = map.get(script);
        if (ms == null) {
          ms = new LinkedList<>();
        }
        ms.add(method);
        map.put(script, ms);
      }
    }
    return map;
  }

  private String getScript(Method method) {
    String value = StormAnnotationUtil.getSelectValue(method);
    if (value != null && value.length() > 0) {
      return value;
    }
    value = StormAnnotationUtil.getDeleteValue(method);
    if (value != null && value.length() > 0) {
      return value;
    }

    value = StormAnnotationUtil.getUpdateValue(method);
    if (value != null && value.length() > 0) {
      return value;
    }

    value = StormAnnotationUtil.getInsertValue(method);
    if (value != null && value.length() > 0) {
      return value;
    }
    return null;
  }

  private Class<?> validClass(List<Class<?>> classes) {
    for (Class<?> aClass : classes) {
      Method[] methods = aClass.getMethods();
      for (Method method : methods) {
        if (hasAnnotated(method)) {
          return aClass;
        }
      }
    }
    return null;
  }

  public boolean hasAnnotated(Method method) {
    if (StormAnnotationUtil.getSelectValue(method) != null) {
      return true;
    }
    if (StormAnnotationUtil.getDeleteValue(method) != null) {
      return true;
    }
    if (StormAnnotationUtil.getInsertValue(method) != null) {
      return true;
    }
    if (StormAnnotationUtil.getUpdateValue(method) != null) {
      return true;
    }
    return false;
  }
}
